#include "PlSDLSimpleNet.h"

#include "simpletcplua.h"
#include "simpleudplua.h"

#include <sstream>
#include <string.h>

/*
class SimpleTCPSlot

SimpleTCPConnection* _parent;
std::string _name;
bool _isRunning;

SDL_mutex* _sendLock;
SDL_mutex* _recvLock;

char* _sendBuffer;
char* _recBuffer;

TCPsocket _sock;
SDL_Thread* _clientThread;
std::vector<IData> _outMessages;
*/

RegisterCppObj<SimpleUDPConnection> g_register_simpleudpconnection;
RegisterCppObj<SimpleTCPConnection> g_register_simpletcpconnection;

std::string debugPrintSerialization(unsigned char* buffer, int datalength)
{
	std::ostringstream bla;
	for(int i = 0; i < datalength; ++i)
	{
		bla << " " << (int)(buffer[i]);
	}
	return bla.str();
}

int PLSDLNetUtils::canParse(unsigned char* buffer, int datalength, int& errornum)
{
	// we need at least 8 bytes (4 magic number, 4 frame size)
	// to have any chance of parsing
	errornum = 0;
	if(datalength < 8)
	{
		LOG(logERROR) << "Not enough data for header.";
		return 0;
	}
	
	// read magic number and frame size
	int magicnum, framesize;
	netReadInt(buffer, magicnum, 0, datalength);
	netReadInt(buffer, framesize, 4, datalength);
	
	if(magicnum != 0xDEADBEEF)
	{
		LOG(logERROR) << "Magic number mismatch when trying to parse frame.";
		errornum = 1;
		return 0;
	}
	
	// otherwise, we can presumably parse it if we have enough data
	if(datalength >= framesize)
	{
		return framesize;
	}
	else
	{
		LOG(logERROR) << "Datalength: " << datalength << ", framesize: " << framesize;
		return 0;
	}
}

IData PLSDLNetUtils::parseAndConsume(unsigned char* buffer, int buffersize, int& bytesconsumed, std::string& msgtypename)
{
	int errnum = 0;
	// read out message size
	int fsize = canParse(buffer, buffersize, errnum);
	int spos = 8;
	// read out the message type name
	std::string msgTypeName;
	spos = netReadShortString(buffer, msgTypeName, spos, buffersize);
	msgtypename = msgTypeName;
	// read out message
	int tbc = 0;
	IData ret = idataFromBytes(buffer + spos, fsize - (spos - 8), tbc);
	bytesconsumed = tbc + spos;
	if(bytesconsumed != fsize)
	{
		LOG(logERROR) << "Frame size mismatch! Expected " << fsize << " but read " << bytesconsumed;
	}
	// now move the unconsumed bytes in the buffer to the front
	int rpos = 0;
	for(int i = bytesconsumed; i < buffersize; ++i, ++rpos)
		buffer[rpos] = buffer[i];
	return ret;
}

int PLSDLNetUtils::serializeMessage(unsigned char* buffer, int buffersize, IData& val, std::string& msgTypeName)
{
	// first, write magic int!
	int magicint = 0xDEADBEEF;
	int spos = 0;
	spos = netWriteInt(buffer, magicint, spos, buffersize);
	//LOG(logDEBUG) << "Magic: " << debugPrintSerialization(buffer, spos);
	// write a placeholder message length!
	int msglen = 0;
	spos = netWriteInt(buffer, msglen, spos, buffersize);
	//LOG(logDEBUG) << "Len: " << debugPrintSerialization(buffer, spos);
	// write message type name
	spos = netWriteShortString(buffer, msgTypeName, spos, buffersize);
	//LOG(logDEBUG) << "Type: " << debugPrintSerialization(buffer, spos);
	// write the idata
	int idatasize = idataToBytes(buffer + spos, buffersize - spos, val);
	spos += idatasize;
	//LOG(logDEBUG) << "Data: " << debugPrintSerialization(buffer, spos);
	// put the message length back in
	msglen = spos;
	netWriteInt(buffer, msglen, 4, buffersize);
	//LOG(logDEBUG) << "Serialized message: " << debugPrintSerialization(buffer, spos);
	return spos;
}

int PLSDLNetUtils::idataToBytes(unsigned char* buffer, int buffersize, IData& val)
{
	int spos = 0;
	std::string tempstr;
	IDataCore* core = val.getRawData();
	
	// here we are writing out a dummy number of fields just to reserve a spot
	unsigned char numfields = 0;
	spos = netWriteByte(buffer, numfields, spos, buffersize);
	
	// now iterate through all the named fields
	std::map<std::string, int>::iterator itr;
	for(itr = core->_nameMap.begin(); itr != core->_nameMap.end();)
	{
		// each field is: type- byte, name- short string, val- (val type)
		std::string name = (*itr).first;
		int fpos = (*itr).second;
		int fieldtype = val.getType(fpos);
		if(fieldtype == IV_OUTOFBOUNDS)
		{
			++itr;
			continue;
		}
		spos = netWriteByte(buffer, (unsigned char)fieldtype, spos, buffersize);
		spos = netWriteShortString(buffer, name, spos, buffersize);
		switch(fieldtype)
		{
		case IV_NONE:
			// nothing to do
			break;
		case IV_INT:
			spos = netWriteInt(buffer, val.geti(fpos), spos, buffersize);
			break;
		case IV_FLOAT:
			spos = netWriteDouble(buffer, val.getf(fpos), spos, buffersize);
			break;
		case IV_STRING:
			tempstr = val.gets(fpos);
			spos = netWriteString(buffer, tempstr, spos, buffersize);
			break;
		case IV_UCHAR:
			spos = netWriteByte(buffer, val.getu(fpos), spos, buffersize);
			break;
		default:
			// nothing to do
			break;
		}		
		
		++numfields;	
		++itr;
		//(*itr)->update(dt);
	}
	
	// finally, put in the actual number of fields
	netWriteByte(buffer, numfields, 0, buffersize);
	
	return spos;
}

IData PLSDLNetUtils::idataFromBytes(unsigned char* buffer, int datasize, int& bytesconsumed)
{
	IData ret;
	// read out how many fields we have
	unsigned char numfields;
	int spos = 0;
	spos = netReadByte(buffer, numfields, spos, datasize);
	std::string tempstr;
	double tempdouble;
	int tempint;
	unsigned char tempchar;
	for(int i = 0; i < numfields; ++i)
	{
		// each field is: type- byte, name- short string, val- (type)
		unsigned char valType = 0;
		std::string fieldName;
		spos = netReadByte(buffer, valType, spos, datasize);
		spos = netReadShortString(buffer, fieldName, spos, datasize);
		switch(valType)
		{
		case IV_NONE:
			// nothing to do
			break;
		case IV_INT:
			spos = netReadInt(buffer, tempint, spos, datasize);
			ret.seti(ret.named(fieldName), tempint);
			break;
		case IV_FLOAT:
			spos = netReadDouble(buffer, tempdouble, spos, datasize);
			ret.setf(ret.named(fieldName), (float)tempdouble);
			break;
		case IV_STRING:
			spos = netReadString(buffer, tempstr, spos, datasize);
			ret.sets(ret.named(fieldName), tempstr);
			break;
		case IV_UCHAR:
			spos = netReadByte(buffer, tempchar, spos, datasize);
			ret.setu(ret.named(fieldName), tempchar);
			break;
		default:
			// nothing to do
			break;
		}
	}
	bytesconsumed = spos;
	return ret;
}

int PLSDLNetUtils::netWriteByte(unsigned char* dest, unsigned char val, int dpos, int buffersize)
{
	dest[dpos] = val;
	return dpos + 1;
}

int PLSDLNetUtils::netWriteDouble(unsigned char* dest, double val, int dpos, int buffersize)
{
	*((double*)(dest + dpos)) = val;
	return dpos + sizeof(double);
}

int PLSDLNetUtils::netWriteInt(unsigned char* dest, int val, int dpos, int buffersize)
{
	*((int*)(dest + dpos)) = val;
	return dpos + sizeof(int);
}

int PLSDLNetUtils::netWriteString(unsigned char* dest, std::string& val, int dpos, int buffersize)
{
	// write full int size
	dpos = netWriteInt(dest, val.size(), dpos, buffersize);
	// copy string
	memcpy((char*)(dest + dpos), val.data(), val.size());
	return dpos + val.size();
}

int PLSDLNetUtils::netWriteShortString(unsigned char* dest, std::string& val, int dpos, int buffersize)
{
	unsigned char writesize = std::min((int)val.size(), (int)255);
	dest[dpos] = writesize;
	memcpy((char*)(dest + dpos + 1), val.data(), writesize);
	return dpos + 1 + writesize;
}

int PLSDLNetUtils::netReadByte(unsigned char* src, unsigned char& val, int spos, int buffersize)
{
	val = src[spos];
	return spos + 1;
}

int PLSDLNetUtils::netReadDouble(unsigned char* src, double& val, int spos, int buffersize)
{
	val = *((double*)(src + spos));
	return spos + sizeof(double);
}

int PLSDLNetUtils::netReadInt(unsigned char* src, int& val, int spos, int buffersize)
{
	val = *((int*)(src + spos));
	return spos + sizeof(int);
}

int PLSDLNetUtils::netReadString(unsigned char* src, std::string& val, int spos, int buffersize)
{
	int templen = 0; //src[spos];
	spos = netReadInt(src, templen, spos, buffersize);
	val = std::string((char*)(src + spos), templen);
	return spos + templen;
}

int PLSDLNetUtils::netReadShortString(unsigned char* src, std::string& val, int spos, int buffersize)
{
	int templen = src[spos];
	val = std::string((char*)(src + spos + 1), templen);
	return spos + 1 + templen;
}

int SimpleTCPSlot::clientMain(void* owner)
{
	SimpleTCPSlot* client = (SimpleTCPSlot *)owner;
	unsigned char* buffer = client->_recBuffer;
	int bufferPos = 0;
	
	// Handle IO from the socket
    while ( client->_isRunning ) 
	{
		// Receive new bytes from socket
		int result = SDLNet_TCP_Recv(client->_sock, buffer + bufferPos, SIMPLE_TCP_SLOT_BUFFER_SIZE - bufferPos - 1);
		if (result < 0)
		{
			LOG(logERROR) << "SDLNet_TCP_Recv (" << client->_name << "): " << SDLNet_GetError();
			client->_parent->killSlot(client, client->_slotId);
			return -1;
		}
		else if (result == 0)
		{
			// Connection was closed
			LOG(logINFO) << "Client disconnected: " << client->_name;
			client->_parent->killSlot(client, client->_slotId);
			return 0;
		}
		bufferPos += result;
		
		// parse as many messages as possible
		int errnum;
		while(client->_isRunning && PLSDLNetUtils::canParse(buffer, bufferPos, errnum))
		{
			std::string tempMessageType;
			int bytesConsumed = 0;
			IData tempID = PLSDLNetUtils::parseAndConsume(buffer, SIMPLE_TCP_SLOT_BUFFER_SIZE, bytesConsumed, tempMessageType);
			MessageContainer bla;
			bla.data = tempID;
			bla.name = tempMessageType;
			SDL_mutexP(client->_recvLock);
			client->_outMessages.push_back(bla);
			SDL_mutexV(client->_recvLock);
			bufferPos -= bytesConsumed;
		}
    }
    
	// Close the client socket
	SDLNet_TCP_Close(client->_sock);
	return 0;
}

SimpleTCPSlot::SimpleTCPSlot(SimpleTCPConnection *parent, TCPsocket sock, std::string name, int slotId)
{
	_parent = parent;
	_sock = sock;
	_name = name;
	_slotId = slotId;
	_isRunning = true;
	
	_sendLock = SDL_CreateMutex();
	_recvLock = SDL_CreateMutex();
	
	_sendBuffer = new unsigned char[SIMPLE_TCP_SLOT_BUFFER_SIZE];
	_recBuffer = new unsigned char[SIMPLE_TCP_SLOT_BUFFER_SIZE];
	
	// Start socket thread (SimpleTCPSlot::clientMain)
	_clientThread = SDL_CreateThread(clientMain, this);
	if ( _clientThread == NULL ) 
	{
		LOG(logERROR) << "Unable to create client thread (" << _name << "): " << SDL_GetError();
		return;
	}
}

const std::string& SimpleTCPSlot::getName()
{
	return _name;
}

void SimpleTCPSlot::setName(const std::string& name)
{
	_name = name;
}

SimpleTCPSlot::~SimpleTCPSlot()
{
	close();
	
	// Destroy mutexes
	LOG(logINFO) << "destroying mutexes and buffers...";
	SDL_DestroyMutex(_sendLock);
	SDL_DestroyMutex(_recvLock);
	delete[] _sendBuffer;
	delete[] _recBuffer;
	LOG(logINFO) << "Done.";
}

void SimpleTCPSlot::close()
{
	if(!_isRunning)
	{
		LOG(logWARNING) << "Slot " << _name << " is already closed.";
		return;
	}
	
	// Flag that the client should shut down and then wait for thread to die
	_isRunning = false;
	
	LOG(logINFO) << "Waiting for thread to die...";
	// Wait for listening thread
	SDLNet_TCP_Close(_sock);
	SDL_WaitThread(_clientThread, NULL);
}

void SimpleTCPSlot::sendMessage(std::string& msgTypeName, IData& msg)
{	
	// Send out message over socket
	SDL_mutexP(_sendLock);
	int msgSize = PLSDLNetUtils::serializeMessage(_sendBuffer, SIMPLE_TCP_SLOT_BUFFER_SIZE, msg, msgTypeName);
	int result = SDLNet_TCP_Send(_sock, _sendBuffer, msgSize);
	SDL_mutexV(_sendLock);
	
	// Upon socket failure, disconnect
	if(result < msgSize) 
	{
		LOG(logERROR) << "SDLNet_TCP_Send (" << _name << "): " << SDLNet_GetError();
		_parent->killSlot(this, _slotId);
	}
}

std::vector<MessageContainer>* SimpleTCPSlot::getMessages()
{
	SDL_mutexP(_recvLock);
	std::vector<MessageContainer>* msgs = NULL;
	if(_outMessages.size() > 0)
	{
		msgs = new std::vector<MessageContainer>;
		*msgs = _outMessages;
		_outMessages.clear();
	}
	SDL_mutexV(_recvLock);
	
	return msgs;
}

SimpleTCPConnection::SimpleTCPConnection()
{
	// just clear out the slots
	for(int i = 0; i < MAX_SIMPLE_TCP_SLOTS; ++i)
		_slots[i] = NULL;
	_isServer = false;
	_listenThread = NULL;
	// initialize SDL if necessary
	if (SDL_Init(0) < 0)
	{
		LOG(logERROR) << "SDL_Init: " << SDL_GetError();
		return;
	}
	
	// initialize SDL_net if necessary
	if (SDLNet_Init() < 0)
	{
		LOG(logERROR) << "SDLNet_Init: " << SDL_GetError();
		return;
	}

}

// SimpleTCPConnection specific stuff
//LIAG_THESE
void SimpleTCPConnection::listenForConnections(int port)
{
	// yeehaw
	_listenport = port;
	
	// Start the listening thread
	_listenThread = SDL_CreateThread(listenMain, this);
    if ( _listenThread == NULL ) 
	{
        LOG(logERROR) << "Unable to create listening thread: " << SDL_GetError();
        return;
    }
	
	// woo
}

void SimpleTCPConnection::connect(std::string& hostname, int port)
{
	if(_isServer)
	{
		LOG(logERROR) << "Servers cannot connect (they listen!)";
		return;
	}
	if(_slots[0] != NULL)
	{
		LOG(logERROR) << "Cannot connect as client; already connected somewhere.";
		return;
	}
	IPaddress ip;
	TCPsocket sock;
	
	/* Resolve the argument into an IPaddress type */
	if(SDLNet_ResolveHost(&ip,hostname.c_str(),port)==-1)
	{
		LOG(logERROR) << "SDLNet_ResolveHost: " << SDLNet_GetError();
		return;
	}

	/* open the server socket */
	sock=SDLNet_TCP_Open(&ip);
	if(!sock)
	{
		LOG(logERROR) << "SDLNet_TCP_Open: " << SDLNet_GetError();
		return;
	}
	_slots[0] = new SimpleTCPSlot(this, sock, "clientconnection", 0);
}

void SimpleTCPConnection::disconnect(int slot)
{
	if(_slots[slot] != NULL)
	{
		delete _slots[slot];
		_slots[slot] = NULL;
	}
}

void SimpleTCPConnection::sendMessage(int targetSlot, std::string& msgTypeName, IData& msg)
{
	if(targetSlot < 0) // send to everyone
	{
		for(int i = 0; i < MAX_SIMPLE_TCP_SLOTS; ++i)
			if(_slots[i] != NULL)
				_slots[i]->sendMessage(msgTypeName, msg);
	}
	else
	{
		if(_slots[targetSlot] != NULL)
			_slots[targetSlot]->sendMessage(msgTypeName, msg);
	}
}

void SimpleTCPConnection::addMessageHandler(std::string& recName, AnyAOPtr obj)
{
	STCMessageHandler mh;
	mh.target = obj;
	mh.targetFunc = recName;
	_msgHandlers.push_back(mh);
}

void SimpleTCPConnection::removeMessageHandler(AnyAOPtr obj)
{
	LOG(logERROR) << "Remove message handler not implemented yet!";
}

void SimpleTCPConnection::addConnectionHandler(std::string& recName, AnyAOPtr obj)
{
	STCMessageHandler mh;
	mh.target = obj;
	mh.targetFunc = recName;
	_connHandlers.push_back(mh);
}

void SimpleTCPConnection::removeConnectionHandler(AnyAOPtr obj)
{
	LOG(logERROR) << "Remove connection handler not implemented yet!";
}

void SimpleTCPConnection::renameConnection(int slot, std::string& newname)
{
	if(slot >= 0 && slot < MAX_SIMPLE_TCP_SLOTS && _slots[slot] != NULL)
		_slots[slot]->setName(newname);
	else
		LOG(logERROR) << "Slot " << slot << " does not have a connection.";
}

std::string SimpleTCPConnection::getConnectionName(int slot)
{
	if(slot >= 0 && slot < MAX_SIMPLE_TCP_SLOTS && _slots[slot] != NULL)
		return _slots[slot]->getName();
	else
	{
		LOG(logERROR) << "Slot " << slot << " does not have a connection.";
		return "ERROR";
	}
}
//STOP_LIAG

void SimpleTCPConnection::killSlot(SimpleTCPSlot* slobj, int id)
{
	if(slobj != NULL && _slots[id] == slobj)
	{
		delete _slots[id];
		_slots[id] = NULL;
	}
	else
	{
		LOG(logERROR) << "Kill slot error!";
	}
}

// General abstract object stuff goes below

// Return the c++ type id
int SimpleTCPConnection::getCppTypeId()
{
	return 1234;
}

// Returns whether this can be cast to a given type
bool SimpleTCPConnection::isType(int id)
{
	return false;
}

// Return the c++ type as a string 
std::string SimpleTCPConnection::getCppTypeName()
{
	return "SimpleTCPConnection";
}

// returns a description of this thing
std::string SimpleTCPConnection::getDescription()
{
	return "Simple TCP message passing facility.";
}

// create another one of these
AbstractObject* SimpleTCPConnection::newInstance()
{
	return new SimpleTCPConnection;
}

// init & update
void SimpleTCPConnection::init()
{
	// nothing special to do here
}

void SimpleTCPConnection::sendMessagesToHandlers(std::vector<MessageContainer>* msgs, int srcslot)
{
	for(unsigned int m = 0; m < msgs->size(); ++m)
	{
		IData& tempid = (*msgs)[m].data;
		// augment the IData with stuff
		tempid.sets(tempid.named("net_msg_type"), (*msgs)[m].name);
		tempid.seti(tempid.named("net_msg_src_slot"), srcslot);
		// send off the message
		for(unsigned int h = 0; h < _msgHandlers.size(); ++h)
		{
			if(*(_msgHandlers[h].target))
				_msgHandlers[h].target->interact(_msgHandlers[h].targetFunc, tempid);
		}
	}
}

void SimpleTCPConnection::update(double dt)
{
	// poll all the slots for messages
	for(int i = 0; i < MAX_SIMPLE_TCP_SLOTS; ++i)
	{
		if(_slots[i] != NULL)
		{
			std::vector<MessageContainer>* msgs = _slots[i]->getMessages();
			if(msgs)
			{
				// send out the messages!
				sendMessagesToHandlers(msgs, i);
				delete msgs;
			}
		}
	}
}

int SimpleTCPConnection::listenMain(void* self)
{
	SimpleTCPConnection* me = (SimpleTCPConnection*)self;
	
	// Resolve the argument into an IPaddress type
	IPaddress ip;
	if(SDLNet_ResolveHost(&ip, NULL, me->_listenport)==-1)
	{
		LOG(logERROR) << "SDLNet_ResolveHost: " << SDLNet_GetError();
		return -1;
	}
	
	// Open the server socket
	TCPsocket server;
	server=SDLNet_TCP_Open(&ip);
	if(!server)
	{
		LOG(logERROR) << "SDLNet_TCP_Open: " << SDLNet_GetError();
		return -1;
	}
	
	// Loop while waiting for clients
	while(me->_isServer)
	{
		// Try to accept a connection
		TCPsocket clientSock;
		clientSock = SDLNet_TCP_Accept(server);
		if(!clientSock)
		{ 
			// No connection accepted, wait for a while
			SDL_Delay(100);
			continue;
		}
		
		// Get client IP and port number
		IPaddress *remoteIp;
		remoteIp = SDLNet_TCP_GetPeerAddress(clientSock);
		if(!remoteIp)
		{
			LOG(logERROR) << "SDLNet_TCP_GetPeerAddress: " << SDLNet_GetError();
			continue;
		}
		Uint32 ipaddr = SDL_SwapBE32(remoteIp->host);

		// Construct the client's name from IP and port
		std::ostringstream clientNameStream;
		clientNameStream <<	((ipaddr>>24) & 0xff) << "." <<
						((ipaddr>>16) & 0xff) << "." <<
						((ipaddr>> 8) & 0xff) << "." <<
						((ipaddr>> 0) & 0xff) << ":" <<
						remoteIp->port;
		std::string clientName = clientNameStream.str();

		// Print out the clients IP and port number
		LOG(logINFO) << "Connection request from " << clientName;
		

		// Start a client slot
		me->addClientSlot(clientSock, clientName);
	}
	
	// Close the listening socket
	SDLNet_TCP_Close(server);
	return 0;
}

void SimpleTCPConnection::addClientSlot(TCPsocket clientSock, std::string clientName)
{
	// find a free slot
	for(int i = 0; i < MAX_SIMPLE_TCP_SLOTS; ++i)
	{
		if(_slots[i] == NULL)
		{
			_slots[i] = new SimpleTCPSlot(this, clientSock, clientName, i);
			return;
		}
	}
	LOG(logERROR) << "Couldn't find free slot!";
}

// push its static functions into the given lua table
void SimpleTCPConnection::pushStaticFuncs(lua_State* L)
{
	// push superclass static functions...
	AbstractObject::pushStaticFuncs(L);

	// push ours...
	SimpleTcpLua::pushStaticFuncs(L);
}

SimpleTCPConnection::~SimpleTCPConnection()
{
	// kill off all slots
	for(int i = 0; i < MAX_SIMPLE_TCP_SLOTS; ++i)
		delete _slots[i];
}

	/*
	std::vector<AnyAOPtr> _handlers;
	bool _isServer;
	SimpleTCPSlot* _slots[MAX_SIMPLE_TCP_SLOTS];
	*/

SimpleUDPConnection::SimpleUDPConnection()
{
	
	// Clear all address slots
	for (int i = 0; i < MAX_SIMPLE_TCP_SLOTS; i++) {
		_peerTable[i].addr.host = INADDR_NONE;
		_peerTable[i].addr.port = -1;
		_peerTable[i].srcID = -1;
	}

	// Clear all inbound connections
	for (int i = 0; i < MAX_SIMPLE_TCP_SLOTS; i++)
	{
		_connTable[i].addr.host = INADDR_NONE;
		_connTable[i].addr.port = -1;
		_connTable[i].sock = NULL;
	}
}

SimpleUDPConnection::~SimpleUDPConnection()
{
	// Close all remaining connections
	for (int i = 0; i < MAX_SIMPLE_TCP_SLOTS; i++)
	{
		if (_connTable[i].sock != NULL)
			closeSocket(_connTable[i]);
	}
	
	// Deallocate packet buffer
	SDLNet_FreePacket(_packet);
}

void SimpleUDPConnection::initNetworking()
{
	// initialize SDL if necessary
	if (SDL_Init(0) < 0)
	{
		LOG(logERROR) << "SDL_Init: " << SDL_GetError();
		return;
	}
	
	// initialize SDL_net if necessary
	if (SDLNet_Init() < 0)
	{
		LOG(logERROR) << "SDLNet_Init: " << SDL_GetError();
		return;
	}

	// Create an outbound connection port
	openSocket(_connTable[0], 0);

	// Create packet buffer
	_packet = SDLNet_AllocPacket(PLSDLNET_MAX_PACKET_SIZE);
	if (_packet == NULL)
		LOG(logERROR) << "Failed to allocate UDP packet buffer: " << SDLNet_GetError();
}

bool SimpleUDPConnection::openSocket(UDPConnection &c, int port)
{
	c.sock = SDLNet_UDP_Open(port);
	if (c.sock == NULL) 
	{
		LOG(logERROR) << "Failed to open UDP connection: " << SDLNet_GetError();
		return false;
	}

	IPaddress *addr = SDLNet_UDP_GetPeerAddress(c.sock, -1);
	if (addr == NULL) {
		LOG(logERROR) << "Failed to retrieve UDP connection address: " << SDLNet_GetError();
		return false;
	}

	c.addr = *addr;
	if(c.addr.host == INADDR_NONE) {
	    LOG(logERROR) << "Failed to decode UDP connection address: " << SDLNet_GetError();
		return false;
	}

	LOG(logDEBUG2) << "Created socket: " << c.addr.host << ":" << c.addr.port << " on socket " << c.sock;
	return true;
}

void SimpleUDPConnection::closeSocket(UDPConnection &c)
{
	SDLNet_UDP_Close(c.sock);
	c.sock = NULL;
	c.addr.host = INADDR_NONE;
	c.addr.port = -1;
}

int SimpleUDPConnection::openConnection(IPaddress& ip, int srcID)
{
	// Check that this connection does not already exist
	for (int i = 0; i < MAX_SIMPLE_TCP_SLOTS; i++)
	{
		if (ip.host == _peerTable[i].addr.host && ip.port == _peerTable[i].addr.port)
			return i;
	}

	// Find the next available address slot
	for (int i = 0; i < MAX_SIMPLE_TCP_SLOTS; i++)
	{	
		if (_peerTable[i].addr.host == INADDR_NONE)
		{
			// Put address info into new slot
			_peerTable[i].addr = ip;
			_peerTable[i].srcID = srcID;
			return i;
		}
	}

	// no free slots....
	LOG(logERROR) << "couldn't open connection; no free slots.";
	return -1;
}

int SimpleUDPConnection::openConnection(std::string& connStr, int port, int srcID)
{
	// connStr will be a string ip address (e.g., "192.168.0.3")
	// connID is the id you should refer to it by with communications to the
	// external world.

	// Resolve server name into address
	IPaddress addr;
	if (SDLNet_ResolveHost(&addr, connStr.c_str(), port) < 0)
	{
		LOG(logERROR) << "Failed to resolve host (" << connStr << ", " << port << "): " << SDLNet_GetError();
		return -1;
	}

	int connId = openConnection(addr, srcID);
	if(connId < 0)
	{
		LOG(logERROR) << "Failed to open (" << connStr << ", " << port << "): too many outgoing connections";
		return -1;
	}
	else
	{
		// give it a name
		std::ostringstream connInfo;
		connInfo << connStr << ":" << port;

		_peerTable[connId].connName = connInfo.str();
		return connId;
	}

	/*
	// Check that this connection does not already exist
	for (int i = 0; i < MAX_SIMPLE_TCP_SLOTS; i++)
	{
		if (addr.host == _peerTable[i].addr.host && addr.port == _peerTable[i].addr.port)
			return i;
	}

	// Find the next available address slot
	for (int i = 0; i < MAX_SIMPLE_TCP_SLOTS; i++)
	{	
		if (_peerTable[i].addr.host == INADDR_NONE)
		{
			// Put address info into new slot
			_peerTable[i].addr = addr;
			_peerTable[i].srcID = srcID;
			return i;
		}
	}*/
}

bool SimpleUDPConnection::allowIncomingConnections(int port)
{
	LOG(logDEBUG) << "Opening port " << port;

	// Check that port is valid
	if (port <= 0) return false;
	if (port > 65535) return false;

	// Ignore if this port is already open
	for (int i = 1; i < MAX_SIMPLE_TCP_SLOTS; i++)
		if (_connTable[i].addr.port == port) return true;

	// Find the next available socket slot
	for (int i = 1; i < MAX_SIMPLE_TCP_SLOTS; i++)
	{	
		if (_connTable[i].sock == NULL) 
		{
			return openSocket(_connTable[i], port);
		}
	}

	LOG(logERROR) << "Failed to open (" << port << "): too many open ports";
	return false;
}

bool SimpleUDPConnection::forbidIncomingConnections(int port)
{
	// Find the relevant socket slot
	for (int i = 0; i < MAX_SIMPLE_TCP_SLOTS; i++) 
	{
		if (_connTable[i].addr.port == port) 
		{
			closeSocket(_connTable[i]);			
			return true;
		}
	}

	LOG(logERROR) << "Failed to close (" << port << "): port not in list";
	return false;
}

void SimpleUDPConnection::closeConnection(int connID)
{
	// Remove the address from our connection table
	_peerTable[connID].addr.host = INADDR_NONE;
	_peerTable[connID].addr.port = -1;
	_peerTable[connID].srcID = -1;
}

void SimpleUDPConnection::update(double dt)
{
	// Iterate through open connections until all incoming packets are handled
	for (int i = 0; i < MAX_SIMPLE_TCP_SLOTS; i++)
	{
		// Get next connection (starting with outbound one)
		if (_connTable[i].sock == NULL) continue;
		while (SDLNet_UDP_Recv(_connTable[i].sock, _packet))
		{
			//LOG(logDEBUG2) << "Got packet: " << _packet->address.host << ":" << _packet->address.port << " of length " << _packet->len;

			// Try to match this packet with a sender
			// Check that this connection does not already exist
			int connID;
			for (connID = 0; connID < MAX_SIMPLE_TCP_SLOTS; connID++)
			{
				if (_packet->address.host == _peerTable[connID].addr.host && 
					_packet->address.port == _peerTable[connID].addr.port) break;
			}

			// If no senders are matched, create a new entry
			if (connID == MAX_SIMPLE_TCP_SLOTS)
			{
				// Get connection info from socket and create entry
				std::string host = std::string(SDLNet_ResolveIP(&(_packet->address)));
				int port = SDLNet_Read16(&(_packet->address.port));
				connID = openConnection(_packet->address, i);

				// give it a name
				std::ostringstream connInfo;
				connInfo << host << ":" << port;

				_peerTable[connID].connName = connInfo.str();
				//LOG(logDEBUG) << "PLSDLNET: Making a new connection named " << _peerTable[connID].connName << " : " << connID;
			}

			//LOG(logDEBUG) << "PLSDLNET: packet from " << _peerTable[connID].connName << " : " << connID;

			// Deserialize data (using freshly allocated memory)
			MessageContainer mc;
			if(deserializePacket(_packet, mc))
			{
				sendMessageToHandlers(mc, _peerTable[connID].connName, connID);
			}
			else
				LOG(logERROR) << "Error deserializing packet.";
		}		
	}

}

void SimpleUDPConnection::sendMessage(int connDest, std::string& msgTypeName, IData& msg, bool reliable)
{
	// do not rely on the memory in msg to be stable after this function call
	//
	// If reliable is true, then the message should be sent reliably, but don't
	// worry about that for now (just send it unreliably).
	MessageContainer tempmc;
	tempmc.data = msg;
	tempmc.name = msgTypeName;
	serializePacket(tempmc, _packet);
	_packet->address = _peerTable[connDest].addr;
	if (!SDLNet_UDP_Send(_connTable[_peerTable[connDest].srcID].sock, -1, _packet))
		LOG(logWARNING) << "Failed to send packet to " << _packet->address.host << ":" << _packet->address.port << ": " << SDLNet_GetError();

	//LOG(logDEBUG2) << "Sent packet: " << _packet->address.host << ":" << _packet->address.port << " of length " << _packet->len;
}

void SimpleUDPConnection::addMessageHandler(std::string& recName, AnyAOPtr obj)
{
	STCMessageHandler mh;
	mh.target = obj;
	mh.targetFunc = recName;
	_msgHandlers.push_back(mh);
}

void SimpleUDPConnection::removeMessageHandler(AnyAOPtr obj)
{
	std::vector<STCMessageHandler>::iterator i2 = _msgHandlers.begin();
	while(i2 != _msgHandlers.end())
	{
		if((*i2).target == obj)
		{
			i2 = _msgHandlers.erase(i2);
		}
		else
			++i2;
	}
}

std::string SimpleUDPConnection::getConnName(int peerid)
{
	if(peerid < 0 || peerid >= MAX_SIMPLE_TCP_SLOTS)
		return "ERROR";
	else
		return _peerTable[peerid].connName;
}

void SimpleUDPConnection::setConnName(int peerid, std::string newname)
{
	if(peerid < 0 || peerid >= MAX_SIMPLE_TCP_SLOTS)
		return;

	_peerTable[peerid].connName = newname;
}

// General abstract object stuff goes below

// Return the c++ type id
int SimpleUDPConnection::getCppTypeId()
{
	return 8456311;
}

// Returns whether this can be cast to a given type
bool SimpleUDPConnection::isType(int id)
{
	return false;
}

// Return the c++ type as a string 
std::string SimpleUDPConnection::getCppTypeName()
{
	return "SimpleUDPConnection";
}

// returns a description of this thing
std::string SimpleUDPConnection::getDescription()
{
	return "A simple (unoptimized) udp message connection.";
}

// create another one of these
AbstractObject* SimpleUDPConnection::newInstance()
{
	return new SimpleUDPConnection;
}

// init & update
void SimpleUDPConnection::init()
{
	// nothing to do
}
//void SimpleUDPConnection::update(double dt);

// push its static functions into the given lua table
void SimpleUDPConnection::pushStaticFuncs(lua_State* L)
{
	// push superclass static functions...
	AbstractObject::pushStaticFuncs(L);

	// push ours...
	SimpleUdpLua::pushStaticFuncs(L);
}
	
void SimpleUDPConnection::serializePacket(MessageContainer& msg, UDPpacket *p)
{
	unsigned char *dest = p->data;
	int msgSize = PLSDLNetUtils::serializeMessage(dest, PLSDLNET_MAX_PACKET_SIZE, msg.data, msg.name);
	//LOG(logDEBUG) << debugPrintSerialization(dest, msgSize);
	p->len = msgSize;
}

bool SimpleUDPConnection::deserializePacket(UDPpacket *p, MessageContainer& msg)
{
	unsigned char *src = p->data;
	int mlen = p->len;
	//LOG(logDEBUG) << debugPrintSerialization(src, mlen);
	int errnum = 0;
	if(	PLSDLNetUtils::canParse(src, mlen, errnum) )
	{
		int bytesConsumed = 0;
		msg.data = PLSDLNetUtils::parseAndConsume(src, mlen, bytesConsumed, msg.name);
		return true;
	}
	else
	{
		LOG(logERROR) << "UDP Packet parsing error: " << errnum;
		msg.name = "ERROR:BAD PACKET";
		return false;
	}
	
}

void SimpleUDPConnection::sendMessageToHandlers(MessageContainer& msg, std::string& srcName, int srcSlot)
{
	IData& tempid = msg.data;
	// augment the IData with stuff
	tempid.sets(tempid.named("net_msg_type"), msg.name);
	tempid.seti(tempid.named("net_msg_src_slot"), srcSlot);
	tempid.sets(tempid.named("net_msg_src_name"), srcName);
	// send off the message
	for(unsigned int h = 0; h < _msgHandlers.size(); ++h)
	{
		if(*(_msgHandlers[h].target))
			_msgHandlers[h].target->interact(_msgHandlers[h].targetFunc, tempid);
	}
}
